package git.soulbgm.utils;

import sun.security.x509.*;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Date;

/**
 * 密钥库生成工具
 * 用于生成包含RSA密钥对的JKS密钥库文件
 *
 * @author SoulBGM
 * @date 2025-03-03
 */
public class KeyStoreGenerator {

    /**
     * RSA 算法名称
     */
    private static final String KEY_ALGORITHM = "RSA";

    /**
     * 生成密钥存储
     *
     * @param keystorePath     密钥库路径
     * @param keystorePassword 密钥库密码
     * @param keyAlias         密钥别名
     * @param keyPassword      密钥密码
     * @param certExpiredDay   证书过期的天数
     * @throws Exception 例外
     */
    public static void generateKeyStore(
            String keystorePath,
            String keystorePassword,
            String keyAlias,
            String keyPassword,
            int certExpiredDay) throws Exception {

        // 判断父路径是否存在 不存在创建目录
        Path path = Paths.get(keystorePath);
        Path parentPath = path.getParent();
        if (!Files.exists(parentPath)) {
            Files.createDirectory(parentPath);
        }

        // 创建密钥库
        KeyStore keyStore = KeyStore.getInstance("JKS");
        if (Files.exists(path)) {
            try (FileInputStream fis = new FileInputStream(keystorePath)) {
                keyStore.load(fis, keystorePassword.toCharArray());
            }
        } else {
            keyStore.load(null, null);
        }

        // 检查别名是否已存在
        if (keyStore.containsAlias(keyAlias)) {
            throw new IllegalArgumentException("密钥别名已存在: " + keyAlias);
        }

        // 生成RSA密钥对
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGenerator.initialize(2048);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 创建自签名证书
        X509Certificate cert = generateCertificate(keyPair, keyAlias, certExpiredDay);

        // 将密钥对和证书存入密钥库
        Certificate[] chain = {cert};
        keyStore.setKeyEntry(keyAlias, keyPair.getPrivate(),
                keyPassword.toCharArray(), chain);

        // 保存密钥库到文件
        try (FileOutputStream fos = new FileOutputStream(keystorePath)) {
            keyStore.store(fos, keystorePassword.toCharArray());
        }
    }

    /**
     * 删除密钥库中的密钥
     *
     * @param keystorePath     密钥库路径
     * @param keystorePassword 密钥库密码
     * @param keyAlias         密钥别名
     * @throws Exception 例外
     */
    public static void deleteEntry(String keystorePath,
                                   String keystorePassword,
                                   String keyAlias) throws Exception {
        // 判断父路径是否存在 不存在创建目录
        Path path = Paths.get(keystorePath);
        if (!Files.exists(path)) {
            throw new IllegalArgumentException(keystorePath + " 的密钥库不存在");
        }

        // 创建密钥库
        KeyStore keyStore = KeyStore.getInstance("JKS");
        try (FileInputStream fis = new FileInputStream(keystorePath)) {
            keyStore.load(fis, keystorePassword.toCharArray());
        }

        // 检查别名是否已存在
        if (keyStore.containsAlias(keyAlias)) {
            keyStore.deleteEntry(keyAlias);
        } else {
            throw new IllegalArgumentException("密钥别名不存在: " + keyAlias);
        }

        // 保存更新后的密钥库
        try (FileOutputStream fos = new FileOutputStream(keystorePath)) {
            keyStore.store(fos, keystorePassword.toCharArray());
        }
    }

    /**
     * 生成证书
     *
     * @param keyPair        配对密码匙
     * @param alias          密钥别名
     * @param certExpiredDay 证书过期的天数
     * @return {@link X509Certificate}
     * @throws Exception 例外
     */
    private static X509Certificate generateCertificate(KeyPair keyPair, String alias, int certExpiredDay) throws Exception {
        // 证书有效期
        long now = System.currentTimeMillis();
        // 一天前
        Date startDate = new Date(now - 24L * 60L * 60L * 1000L);
        // 指定天数后
        Date endDate = new Date(now + (certExpiredDay * 24L * 60L * 60L * 1000L));

        // 证书序列号
        BigInteger serialNumber = BigInteger.valueOf(now);

        // 创建证书信息
        X509CertInfo info = new X509CertInfo();

        // 设置有效期
        CertificateValidity interval = new CertificateValidity(startDate, endDate);
        info.set(X509CertInfo.VALIDITY, interval);

        // 设置序列号
        info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serialNumber));

        // 创建X500Name对象（使用sun.security.x509.X500Name）
        X500Name owner = new X500Name("CN=" + alias);

        // 设置主题信息
        info.set(X509CertInfo.SUBJECT, owner);

        // 设置颁发者信息（自签名证书，颁发者和主题相同）
        info.set(X509CertInfo.ISSUER, owner);

        // 设置公钥
        info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));

        // 设置算法ID
        AlgorithmId algorithm = new AlgorithmId(AlgorithmId.sha256WithRSAEncryption_oid);
        info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algorithm));

        // 设置版本号
        info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));

        // 创建证书并签名
        X509CertImpl cert = new X509CertImpl(info);
        cert.sign(keyPair.getPrivate(), "SHA256withRSA");

        return cert;
    }

    public static void main(String[] args) {
        try {
            // 通过 keytool -list -v -keystore rsa-keypair.jks 进行查看密钥库

            //密钥库路径
            String keystorePath = "src/main/resources/keystore/rsa-keypair.jks";
            //密钥库密码
            String keystorePassword = "store_99G1";
            //密钥别名
            String keyAlias = "login_key";
            //密钥密码
            String keyPassword = "99G1_rsa";
            //多少天后证书过期         这里填写的是1000年后  根据实际情况填写
            int certExpiredDay = 1000 * 365;
            generateKeyStore(keystorePath, keystorePassword, keyAlias, keyPassword, certExpiredDay);
            System.out.println("密钥库生成成功！");
            /*deleteEntry(keystorePath, keystorePassword, "login_key2");
            System.out.println("密钥库删除成功！");*/
        } catch (Exception e) {
            System.err.println("生成密钥库失败：" + e.getMessage());
            e.printStackTrace();
        }
    }
}